﻿using System;
using System.Collections.Generic;

namespace personalplaner.common.mvvm.common
{
    /// <summary>
    /// Implements a simple service locator
    /// </summary>
    public class ServiceLocator : IServiceProvider
    {
		static object locker = new object();

		static Dictionary<Type, object> globalServices;
		Dictionary<Type, object> services = new Dictionary<Type, object>();

		static Dictionary<Type, object> GlobalServices
		{
			get
			{
				if (globalServices == null)
					globalServices = new Dictionary<Type, object>();
				return globalServices;
			}
		}

		#region IServiceProvider Members

		/// <summary>
		/// Gets a service from the service locator
		/// </summary>
		/// <typeparam name="T">The type of service you want to get</typeparam>
		/// <returns>Returns the instance of the service</returns>
		public T GetService<T>()
		{
			return (T)GetService(typeof(T));
		}

        /// <summary>
        /// gets a service from the servicelocator. 
        /// if the service doesn't exist a new instance is created
        /// </summary>
        /// <typeparam name="T">type of service you want to get</typeparam>
        /// <typeparam name="O">type of instance that will be registered with the type</typeparam>
        /// <returns></returns>
        public T GetService<T, O>() where O : class, new()
        {
            if (!services.ContainsKey(typeof(T)) && !GlobalServices.ContainsKey(typeof(T)))
            {
                services.Add(typeof(T), new O());
            }

            return GetService<T>();
        }

		/// <summary>
		/// Registers a service to the service locator
		/// </summary>
		/// <param name="serviceType">The type of service to register. This is used so that you can register the service by an interface that the object implements</param>
		/// <param name="service">The service to add</param>
		/// <param name="overwriteIfExists">Passing true will replace any existing service</param>
		/// <returns>Returns true if the service was successfully registered</returns>
		/// <remarks>
		///     <para>This generics based implementation ensures that the service must at least inherit from the service type.</para>
		///     <para>NOTE: the MSDN documentation on IServiceProvidor states that the GetService method returns an object of type servieProvider</para>
		/// </remarks>
		public bool RegisterService<T>(T service, bool overwriteIfExists)
		{
			//lock (services)
			lock (locker)
			{
				if (!services.ContainsKey(typeof(T)))
				{
					services.Add(typeof(T), service);
					return true;
				}
				else if (overwriteIfExists)
				{
					services[typeof(T)] = service;
					return true;
				}
			}
			return false;
		}

		/// <summary>
		/// Registers a service to the service locator. This will overwrite any registered services with the same registration type
		/// </summary>
		/// <param name="serviceType">The type of service to register. This is used so that you can register the service by an interface that the object implements</param>
		/// <param name="service">The service to add</param>
		/// <returns>Returns true if the service was successfully registered</returns>
		/// <remarks>
		///     <para>This generics based implementation ensures that the service must at least inherit from the service type.</para>
		///     <para>NOTE: the MSDN documentation on IServiceProvidor states that the GetService method returns an object of type servieProvider</para>
		/// </remarks>
		public bool RegisterService<T>(T service)
		{
			return RegisterService<T>(service, true);
		}

		/// <summary>
		/// Gets a service from the service locator
		/// </summary>
		/// <param name="serviceType">The type of service you want to get</param>
		/// <returns>Returns the instance of the service</returns>
		/// <remarks>This implements IServiceProvider</remarks>
		public object GetService(Type serviceType)
		{
			//lock (services)
			lock (locker)
			{
				if (services.ContainsKey(serviceType))
				{
					return services[serviceType];
				}
				else if (GlobalServices.ContainsKey(serviceType))
				{
					return GlobalServices[serviceType];
				}
			}
			return null;
		}

		#endregion

		#region Global Services

		public static bool RegisterGlobalService<T>(T service)
		{
			return RegisterGlobalService<T>(service, true);
		}

		public static bool RegisterGlobalService<T>(T service, bool overwriteIfExists)
		{
			lock (locker)
			{
				if (!GlobalServices.ContainsKey(typeof(T)))
				{
					GlobalServices.Add(typeof(T), service);
					return true;
				}
				else if (overwriteIfExists)
				{
					GlobalServices[typeof(T)] = service;
					return true;
				}
			}
			return false;
		}

		/// <summary>
		/// Gets a service from the service locator
		/// </summary>
		/// <typeparam name="T">The type of service you want to get</typeparam>
		/// <returns>Returns the instance of the service</returns>
		public static T GetGlobalService<T>()
		{
			return (T)GetGlobalService(typeof(T));
		}

        /// <summary>
        /// gets a service from the global servicelocator. 
        /// if the service doesn't exist a new instance is created
        /// </summary>
        /// <typeparam name="T">type of service you want to get</typeparam>
        /// <typeparam name="O">type of instance that will be registered with the type</typeparam>
        /// <returns></returns>
        public static T GetGlobalService<T, O>() where O : class, new()
        {
            lock (locker)
            {
                if (!GlobalServices.ContainsKey(typeof(T)))
                {
                    GlobalServices.Add(typeof(T), new O());
                }
            }

            return GetGlobalService<T>();
        }

		public static object GetGlobalService(Type serviceType)
		{
			//lock (services)
			lock (locker)
			{
				if (GlobalServices.ContainsKey(serviceType))
				{
					return GlobalServices[serviceType];
				}
			}
			return null;
		}

		#endregion
    }
}
